/*
 * Copyright (C) 2010, JDevelopers (jdevelopers@my-pages.org) 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 */
package org.jdevelopers.xarf;

import java.io.IOException;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.mail.Header;
import javax.mail.MessagingException;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.SharedByteArrayInputStream;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.validator.EmailValidator;
import org.apache.commons.validator.UrlValidator;
import org.apache.commons.validator.util.Flags;
import org.jdevelopers.xarf.exceptions.InvalidSchemaException;
import org.jdevelopers.xarf.exceptions.InvalidSchemaLocationException;
import org.jdevelopers.xarf.exceptions.SchemaReadException;
import org.jdevelopers.xarf.exceptions.XarfValidatorException;
import org.jdevelopers.xarf.util.ClassFactory;

/**
 * Class for parsing and validating the report message. You can specify, if you want to check the report, if it is an 
 * arf report. If the message is an arf report, then we will load our own JSON definition, because for ARF messages, 
 * there is no JSON schema available. The JSON schema refers to the definitions in the RFC 5965 (http://www.rfc-editor.org/rfc/rfc5965.txt).
 * 
 * @author Marcel Hartwig
 * @version 0.3
 * @since JXarfValidator 0.2
 */
public class XarfValidator {
	
	/**
	 * Allows to verify the MimeMessages against a x-arf schema only.
	 */
	public static final int ALLOW_ONLY_XARF_PARSING = 1 << 0;
	
	/**
	 * Allows to verify a MimeMessage against a x-arf schema and a normal ARF abuse message.
	 */
	public static final int ALLOW_XARF_AND_ARF_PARSING = 1 << 1;
	
	/**
	 * Allows the use of the scheme cache.
	 */
	public static final int USE_SCHEME_CACHE = 1 << 2;
	
	/**
	 * Holds the set of current validator options.
	 */
	private Flags option;
	
	/**
	 * The default instance of this class without any options.
	 */
	private static XarfValidator defaultInstance = new XarfValidator();
	
	/**
	 * The scheme cache for caching the parsed schemes.
	 */
	private static Map<String, XarfSchema> cachedSchemes = new HashMap<String, XarfSchema>();
	
	/**
	 * The instance of the UrlValidator for validating URL's.
	 */
	private static UrlValidator urlValidator = null;
	
	/**
	 * Pattern to find our Message-ID in the body of the attached message.
	 */
	private static final Pattern MESSAGE_ID_PATTERN = Pattern.compile("Message-ID: (<.*?>)");
	
	/**
	 * Create a XarfValidator with default properties.
	 */
	public XarfValidator() {
		this(ALLOW_ONLY_XARF_PARSING);
	}
	
	/**
	 * Initialize a XarfValidator with the given parsing options.
	 * 
	 * @param option The option should be set using the public constants declared in 
	 * this class. To set multiple options you simply add them together. For example,
     * ALLOW_ONLY_XARF_PARSING + USE_SCHEME_CACHE enables both of those options.
	 */
	public XarfValidator(int option) {
		this.option = new Flags(option);
	}
	
	public static XarfValidator getDefaultInstance() {
		return defaultInstance;
	}
	
	/**
	 * Method, which does the whole work. This method parses the report message into 
	 * a XarfReport and does certain checks.
	 * 
	 * @param message the MimeMessage, which should be parsed.
	 * @return the parsed XarfReport.
	 * @throws XarfValidatorException if a error occurs while parsing the message.
	 * @throws ClassNotFoundException if a n error occurs while resolving class names.
	 * @throws InvalidSchemaException if the given schema location is invalid.
	 * @throws InvalidSchemaLocationException if the given schema location is invalid.
	 * @throws SchemaReadException if there is some trouble while reading the schema from the schema location.
	 */
	public XarfReport parseEmail(MimeMessage message) throws XarfValidatorException, ClassNotFoundException, InvalidSchemaException, InvalidSchemaLocationException, SchemaReadException {
		EReportType reportType = checkMailStructure(message);
		XarfReport report = null;
		try {
			
			//Human readable part of the report
			MimeBodyPart bodyPart = (MimeBodyPart) ((MimeMultipart) message.getContent()).getBodyPart(0);
			
			if (reportType == EReportType.HOTMAIL_RFC822) {
			    return parseHotmailFeedback(message);
			}
			
			report = new XarfReportImpl(reportType);
			report.setHumanReadablePart((String) bodyPart.getContent());
			
			//Yaml-report
			bodyPart = (MimeBodyPart) ((MimeMultipart) message.getContent()).getBodyPart(1);
			
			//parsing the YAML-report.
			Map<String, List<String>> reportContent = extractYamlreport(bodyPart, reportType);
			
			if(reportType == EReportType.XARF_REPORT && !reportContent.containsKey("Schema-URL")) {
				throw new XarfValidatorException("Yaml-Report does not contains a \"Schema-URL\". Could not verify the report.");
			} else if(reportType == EReportType.ARF_REPORT) {
				List<String> tmpList = new Vector<String>();
				tmpList.add("http://jdevelopers.my-pages.org/email-feedback-format_0.1.0.json");
				reportContent.put("Schema-URL", tmpList);
			}
			
			//loading the schema
			XarfSchema xarfSchema;
			if(this.option.isOn(USE_SCHEME_CACHE) && cachedSchemes.containsKey(reportContent.get("Schema-URL"))) {
				xarfSchema = cachedSchemes.get(reportContent.get("Schema-URL"));
			} else {
				//schema not in cache, or caching not enabled
				xarfSchema = XarfSchemaFactory.getXarfSchema(reportContent.get("Schema-URL").get(0));
				
				//adding the schema to the cache, so schema could be loaded from cache, if needed.
				if(!cachedSchemes.containsKey(reportContent.get("Schema-URL"))) {
					cachedSchemes.put(reportContent.get("Schema-URL").get(0), xarfSchema);
				}	
			}
			
			//checking the properties in details
			StringBuilder errorMessages = new StringBuilder();
			for(XarfSchemaProperty property : xarfSchema.getXarfProperties()) {
				errorMessages.append(checkProperty(xarfSchema, property, reportContent, false));
			}
			
			//throwing the error messages, if something went wrong while checking the properties
			if(errorMessages.length() > 0) {
				throw new XarfValidatorException(errorMessages.toString().replaceFirst("\r\n$", ""));
			}

			//adding the report properties to the result
			
			for(String propertyName : reportContent.keySet()) {
				if(xarfSchema.getXarfProperty(propertyName) != null) {
					report.addReportProperty(new XarfReportPropertyImpl(propertyName, reportContent.get(propertyName), 
							xarfSchema.getXarfProperty(propertyName).getPropertyFormatType()));
				}
			}
			report.setReportSubject(message.getSubject());
			report.setReportSender(message.getSender());
			
			//checking the attachment from the report
			XarfSchemaProperty property = xarfSchema.getXarfProperty("Attachment");
			if (property != null && !property.isOptional() && ((MimeMultipart) message.getContent()).getCount() < 3) {
				//no attachment in report, checking, if the value "none" is possible 
				if(property.isValuePossible("none")) {
					//everything OK
					report.setAttachmentType(EXarfReportAttachmentType.NONE);
				} else {
					//value "none" not possible
					throw new XarfValidatorException("Missing the mandatory attachment part for the report.");
				}
			} else if(property != null && ((MimeMultipart) message.getContent()).getCount() > 2) {
				//getting the attachment from the message
				bodyPart = (MimeBodyPart) ((MimeMultipart) message.getContent()).getBodyPart(2);
				
				//checking content type for the attachment
				String contentType = bodyPart.getContentType().split(";")[0].trim();
				if(property.isValuePossible(contentType)) {
					//valid attachment type
					report.setAttachmentType(EXarfReportAttachmentType.getByValue(contentType));
					switch (report.getAttachmentType()) {
						case TEXT_PLAIN:
							report.setTextAttachment((String)bodyPart.getContent());
							break;
						case MESSAGE_RFC822:
							report.addAllXarfReportAttachmentMailHeaders(getAttachmentHeaders((MimeMessage)bodyPart.getContent()));
							break;
					}
				} else {
					//content type is not valid for the attachment
					throw new XarfValidatorException("Attachment type \"" + contentType + "\" is not supported for attachments.");
				}
			} else if(reportType == EReportType.ARF_REPORT) {
				if(((MimeMultipart) message.getContent()).getCount() < 3) {
					throw new XarfValidatorException("Missing the mandatory attachment part for the report.");
				} else {
					bodyPart = (MimeBodyPart) ((MimeMultipart) message.getContent()).getBodyPart(2);
					String contentType = bodyPart.getContentType().split(";")[0].trim();
					report.setAttachmentType(EXarfReportAttachmentType.getByValue(contentType));
					
					switch (report.getAttachmentType()) {
						case MESSAGE_RFC822:
							report.addAllXarfReportAttachmentMailHeaders(getAttachmentHeaders((MimeMessage)bodyPart.getContent()));
							break;
					}
				}
			}
			
		} catch (MessagingException e) {
			throw new XarfValidatorException("Error processing the report message!", e);
		} catch (IOException e) {
			throw new XarfValidatorException("Error processing the report message!", e);
		}
		return report;
	}
	
	/**
	 * Special Method to parse abuse mails from Hotmail. Hotmail doesn't send an ARF-Report. So we have to analyze the mail manually.
	 * 
	 * @param message Message to be parsed.
	 * @return XarfReport, which contains all necessary informations.
	 * @throws XarfValidatorException if a error occurs while parsing the message.
	 */
	private XarfReport parseHotmailFeedback(MimeMessage message) throws XarfValidatorException {
	    //Create new report
	    XarfReport report = new XarfReportImpl(EReportType.HOTMAIL_RFC822);
	    
	    //set human readable information
	    report.setHumanReadablePart("E-Mail does not contains any human readable part, because this a feedback from hotmail.");
	    try {
	        //Set subject of the message
            report.setReportSubject(message.getSubject());
            //Set sender of the abuse message
            report.setReportSender(message.getSender());
            //set attachment type of abuse message
            report.setAttachmentType(EXarfReportAttachmentType.MESSAGE_RFC822);
            
            //Set necessary informations about the report
            report.addReportProperty(new XarfReportPropertyImpl("Received-Date", message.getHeader("Date")[0], EXarfPropertyFormatType.DATE_TIME));
            report.addReportProperty(new XarfReportPropertyImpl("Feedback-Type", "abuse", EXarfPropertyFormatType.NOT_SPECIFIED));
            report.addReportProperty(new XarfReportPropertyImpl("User-Agent", "Hotmail", EXarfPropertyFormatType.NOT_SPECIFIED));
            
            //Add all headers of the attached message
            report.addAllXarfReportAttachmentMailHeaders(
                        getAttachmentHeaders((MimeMessage)((MimeBodyPart) ((MimeMultipart) message.getContent()).getBodyPart(0)).getContent())
                    );
        } catch (MessagingException e) {
            throw new XarfValidatorException("Error processing the report message!", e);
        } catch (IOException e) {
            throw new XarfValidatorException("Error processing the report message!", e);
        }
        //return the created report.
        return report;
    }

    /**
	 * Method for fetching all MailHeaders from the attached message.
	 * 
	 * @param message the attached message.
	 * @return A list of XarfReportAttachmentMailHeaders.
	 * @throws MessagingException If an error occurs while reading the headers of the mail.
	 */
	private List<XarfReportAttachmentMailHeader> getAttachmentHeaders(MimeMessage message) throws MessagingException {
		List<XarfReportAttachmentMailHeader> result = new Vector<XarfReportAttachmentMailHeader>();
		Enumeration<?> myEnum = message.getAllHeaders();
		XarfReportAttachmentMailHeader header;
		boolean messageIdFound = false;
		while(myEnum.hasMoreElements()) {
			Header mailHeader = (Header)myEnum.nextElement();
			header = new XarfReportAttachmentMailHeaderImpl();
			header.setHeaderName(mailHeader.getName());
			header.setHeaderValue(mailHeader.getValue());
			
			if (mailHeader.getName().equalsIgnoreCase("Message-ID")) {
			    messageIdFound = true;
			}
			
			result.add(header);
		}
		
		//Road-Runner does not Add's the Message-ID to the header of the Mail, we have to parse is out of the text
		if (!messageIdFound) {
            try {
                final Matcher messageIdMatcher = MESSAGE_ID_PATTERN.matcher((String) message.getContent());
                if (messageIdMatcher.find()) {
                    header = new XarfReportAttachmentMailHeaderImpl();
                    header.setHeaderName("Message-ID");
                    header.setHeaderValue(messageIdMatcher.group(1));
                    result.add(header);                    
                }
            } catch (IOException ignore) { }
		}
		return result;
	}
			
	/**
	 * Method for checking, if the value of the property in the report matches the conditions in the XarfSchemaPropery. This 
	 * method calls itself recursively, if there is a depencyCheck needed.
	 * 
	 * @param xarfSchema the schema for the validation.
	 * @param propertyToCheck the XarfSchemaProperty, that needs to be checked.
	 * @param reportValues the content of the the Yaml-Report.
	 * @param depencyCheck <code>true</code>, is this a dependency check, otherwise <code>false</code>
	 * @return String with error messages.
	 * @throws XarfValidatorException if some error occurs while checking the report values.
	 * @throws ClassNotFoundException if a n error occurs while resolving class names. 
	 */
	private String checkProperty(XarfSchema xarfSchema, XarfSchemaProperty propertyToCheck, Map<String, List<String>> reportValues, boolean dependencyCheck) throws XarfValidatorException, ClassNotFoundException  {
		StringBuilder errorMessages = new StringBuilder();
		boolean isValid = true;
		//Only perform the check, if the property is mandatory, or the property is optional but there is an other property set, that 
		//needs an other optional property or the property is optional, but set in the message
		if(!propertyToCheck.isOptional() || dependencyCheck || (propertyToCheck.isOptional() 
				&& reportValues.containsKey(propertyToCheck.getPropertyName().toLowerCase()))) {
			if(!reportValues.containsKey(propertyToCheck.getPropertyName().toLowerCase())) {
				//the property is not set in the message
				errorMessages.append("Report does not contains the mandatory property: ")
					.append(propertyToCheck.getPropertyName()).append("\r\n");
				isValid = false;
			} else if(propertyToCheck.hasPossibleValues()) { 
				for(String content : reportValues.get(propertyToCheck.getPropertyName().toLowerCase())) {
					if(!propertyToCheck.isValuePossible(content)) {
						errorMessages.append("In the schema at property \"").append(propertyToCheck.getPropertyName())
							.append("\" is no value like \"").append(reportValues.get(propertyToCheck.getPropertyName().toLowerCase())).append("\".\r\n");
						isValid = false;
					}
					if(!isValid) {
						break;
					}
				}
				//the value of the property in the message has an attribute, that is not allowed
				
			}

			if(!propertyToCheck.hasMultipleOccurrence() && reportValues.get(propertyToCheck.getPropertyName().toLowerCase()).size() > 1) {
				errorMessages.append("The property \"" + propertyToCheck.getPropertyName() + "\" occurred muliple times in the report.\r\n");
				isValid = false;
			}
			
			if(isValid) {
				for(String content : reportValues.get(propertyToCheck.getPropertyName().toLowerCase())) {
					//check, if the syntax of the properties value is correct
					switch (propertyToCheck.getPropertyFormatType()) {
						case DATE_TIME:
							//check, if the value is a date in the RFC3339 format
							//TODO: Implement: parsing the date
							break;
						case EMAIL:
							//check, if the value is a syntactly correct email address
						    //we accept, if there is no email address given
							if(content.replaceAll("[<>]", "").length() > 0 
							        && !EmailValidator.getInstance().isValid(content.replaceAll("[<>]", ""))) {
								errorMessages.append("The email \"").append(reportValues.get(propertyToCheck.getPropertyName().toLowerCase()))
									.append("\" for the property \"").append(propertyToCheck.getPropertyName())
									.append("\" is not a valid email address.\r\n");
								isValid = false;
							}
							break;
						case URI:
							//check, if the value is a syntactly correct URI
							if(urlValidator == null) {
								urlValidator = new UrlValidator(UrlValidator.ALLOW_ALL_SCHEMES);
							}
							if(!urlValidator.isValid(content)) {
								errorMessages.append("The uri \"").append(reportValues.get(propertyToCheck.getPropertyName().toLowerCase()))
									.append("\" for the property \"").append(propertyToCheck.getPropertyName())
									.append("\" is not a valid uri");
								isValid = false;
							}
							break;
					}
					if(!isContentValid(propertyToCheck.getPropertyType(), content)) {
						errorMessages.append("The content of the property \"").append(propertyToCheck.getPropertyName())
							.append("\" does not match the content type \"")
							.append(ClassFactory.getClassName(propertyToCheck.getPropertyType())).append("\".\r\n");
					}
				}
			}
			
		}
		
		if(propertyToCheck.isAPropertyRequired() && isValid && reportValues.containsKey(propertyToCheck.getPropertyName().toLowerCase())) {
			//checks other properties, if there is a required property
			errorMessages.append(checkProperty(xarfSchema, propertyToCheck.getRequiredProperty(), reportValues, true));
		}
		return errorMessages.toString();
	}

	/**
	 * Checks, if the content matches the specified content type of the JSON-schema.
	 * 
	 * @param classType the Class-type, specified in the JSON-schema
	 * @param content the content from the  X-ARF report.
	 * @return <code>true</code> if the content matches the content type, otherwise <code>false</code>
	 * @throws XarfValidatorException if a content type should be checked, that is not supported.
	 */
	private boolean isContentValid(Class<?> classType, String content) throws XarfValidatorException {
		if (classType.equals(Integer.class)) {
			try {
				Integer.parseInt(content);
			} catch (NumberFormatException nfe) {
				return false;
			}
		} else if (classType.equals(Number.class)) {
			try {
				NumberFormat.getInstance().parse(content);
			} catch (ParseException e) {
				return false;
			}
		} else if (classType.equals(String.class)) {
			return true;
		} else {
			throw new XarfValidatorException("Tried to varify a content type, that is not supported.");
		}
		return true;
	}
	
	/**
	 * Method which processes the Yaml-Report and extracts all Properties and the corresponding value to the property. 
	 * 
	 * @param bodyPart the Yaml-Report as MimeBodyPart of the report message.
	 * @param reportType the type of the report.
	 * @return returns a Map, which contains the Property-Name as Key and the Property-Value as Value of the Map.
	 * @throws XarfValidatorException if an error occurs while reading the Yaml-Report.
	 */
	private Map<String, List<String>> extractYamlreport(MimeBodyPart bodyPart, EReportType reportType) throws XarfValidatorException {
		Map<String, List<String>> reportContent = new HashMap<String, List<String>>();
		boolean isFirstLine = true;
		String[] splittedContent;
		try {
			String report;
			List<String> content;
			if(reportType == EReportType.ARF_REPORT) {
				report = new Scanner((SharedByteArrayInputStream)bodyPart.getContent()).useDelimiter("\\Z").next();
			} else {
				report = (String)bodyPart.getContent();
			}
			for(String reportLine : (report).trim().split("\r?\n")) {
				if(reportType == EReportType.XARF_REPORT && isFirstLine) {
					if(!reportLine.equals("---")) {
						throw new XarfValidatorException("Report does not start with \"---\"");
					}
					isFirstLine = false;
					continue;
				}
				splittedContent = reportLine.split(":");
				content = reportContent.get(splittedContent[0].trim());
				if(content == null) {
					content = new Vector<String>();
				}
				content.add(StringUtils.join(splittedContent, ":", 1, splittedContent.length).trim());
				reportContent.put(splittedContent[0].trim().toLowerCase(), content);
			}
		} catch (IOException e) {
			throw new XarfValidatorException("Error processing the report message!", e);
		} catch (MessagingException e) {
			throw new XarfValidatorException("Error processing the report message!", e);
		}
		return reportContent;
	}

	/**
	 * Method for checking, if the message to be processed is a correct x-arf or arf report. If the message is 
	 * no correct x-arf or arf report, a Exception will be thrown, which contains the corresponding error.
	 * 
	 * @param message the message to be processed.
	 * @return EReportType.XARF_REPORT, if it is x-arf report or EReportType.ARF_REPORT, if it is a arf report. 
	 * @throws XarfValidatorException the corresponding error, if the message is not a x-arf report.
	 */
	private EReportType checkMailStructure(MimeMessage message) throws XarfValidatorException {
		EReportType reportType = EReportType.XARF_REPORT;
		try {
			//check, if the header "X-ARF" is set in the message. If it is no x-arf report, then checking for an arf report.
			if(message.getHeader("X-ARF") == null && this.option.isOn(ALLOW_XARF_AND_ARF_PARSING)) {
				if(message.getHeader("Content-Type") == null || message.getHeader("Content-Type").length == 0 
						|| message.getHeader("Content-Type")[0].indexOf("feedback-report") == -1) {
				    if (message.getFrom()[0].toString().equals("staff@hotmail.com")) {
				        //Hotmail do not send ARF- or XARF-reports. Hotmail always forwards sends an message
				        //with a RFC822-Attachment. The sender of this mail is ALWAYS staff@hotmail.com
				        //Here we have to parse the attachment.
				        reportType = EReportType.HOTMAIL_RFC822;
				    } else {				        
				        throw new XarfValidatorException("This email is no xarf or arf report!");
				    }
				} else {
					reportType = EReportType.ARF_REPORT;
				}
			} else if(message.getHeader("X-ARF") == null && this.option.isOff(ALLOW_XARF_AND_ARF_PARSING)) {
				throw new XarfValidatorException("This email is no xarf report!");
			} else if (message.getHeader("X-ARF").length != 1 && !message.getHeader("X-ARF")[0].equalsIgnoreCase("yes")) {
			
				throw new XarfValidatorException("This email is no xarf report!");
			}
		
			//Check, if there is a human readable part in the message
			MimeBodyPart bodyPart = (MimeBodyPart) ((MimeMultipart) message.getContent()).getBodyPart(0);
			if(bodyPart == null) {
				throw new XarfValidatorException("Message contains no human readable report part.");
			}
			//Check the content type of the human readable part in the message. The Content has to be encoded in UTF-8, if it is an x-arf report.
			if(reportType == EReportType.XARF_REPORT) {
				if(bodyPart.getContentType() == null) {
					throw new XarfValidatorException("Human readable report part contains no content-type.");
				}
				if(!bodyPart.getContentType().toLowerCase().contains("utf8") && !bodyPart.getContentType().toLowerCase().contains("utf-8")) {
					throw new XarfValidatorException("Charset of human readable report part is not in UTF8 or UTF-8.");
				}
			}
			
			if (reportType != EReportType.HOTMAIL_RFC822) {
    			//Check, if there is a Yaml-Report attached to the message. The character set of the Yaml-Report has to be UTF-8 
    			//and the name of the attachment has to be report.(txt|yaml|yml)
    			bodyPart = (MimeBodyPart) ((MimeMultipart) message.getContent()).getBodyPart(1);
    			if(bodyPart == null) {
    				throw new XarfValidatorException("Message contains no Yaml-Report.");
    			}
    			
    			if(reportType == EReportType.XARF_REPORT) {
    				if(bodyPart.getContentType() == null) {
    					throw new XarfValidatorException("Charset of Yaml-Report is not set.");
    				}
    				if(!bodyPart.getContentType().toLowerCase().contains("utf8") && !bodyPart.getContentType().toLowerCase().contains("utf-8")) {
    					throw new XarfValidatorException("Charset of Yaml-Report is not in UTF8 or UTF-8");
    				}
    				if(!bodyPart.getContentType().toLowerCase().contains("report.txt") 
    						&& !bodyPart.getContentType().toLowerCase().contains("report.yaml") 
    						&& !bodyPart.getContentType().toLowerCase().contains("report.yml")) {
    					throw new XarfValidatorException("The name of the Yaml-Report is not report.(txt|yaml|yml).");
    				}
    			}
    			
    			if(reportType == EReportType.ARF_REPORT) {
    				bodyPart = (MimeBodyPart) ((MimeMultipart) message.getContent()).getBodyPart(2);
    				if(bodyPart == null) {
    					throw new XarfValidatorException("Message contains attachment.");
    				}
    			}
			} else {
			    bodyPart = ((MimeBodyPart) ((MimeMultipart) message.getContent()).getBodyPart(0));
			    if(bodyPart == null) {
                    throw new XarfValidatorException("Message is a hotmail abuse message, but contains no body.");
                }
			    if (!bodyPart.getContentType().equalsIgnoreCase("message/rfc822")) {
			        throw new XarfValidatorException("Message is a hotmail abuse message, contains no message/rfc822");
			    }
			    try {
			         MimeMessage messageAttachment = (MimeMessage) bodyPart.getContent();
			         if (messageAttachment.getHeader("Message-ID") == null || 
			                 messageAttachment.getHeader("Message-ID").length == 0) {
			             throw new XarfValidatorException("Attached Message does not conains a message-ID");
			         }
			    } catch (Exception e) {
                    throw new XarfValidatorException("Error getting message attachment", e);
                }
			    
			}
		} catch (MessagingException e) {
			throw new XarfValidatorException("Error processing the report message!", e);
		} catch (IOException e) {
			throw new XarfValidatorException("Error processing the report message!", e);
		}
		return reportType;
	}
	
}
